#ifndef _DeviceManager_H_
#define _DeviceManager_H_

#include "../GCMemoryManager.h"

#if defined(DIRECT3D10) && !defined(PS3)
#define DEVMAN_USE_STAGING_POOL
#endif

class CDeviceTexture;
class CDeviceVertexBuffer;
class CDeviceIndexBuffer;

typedef CDeviceTexture* LPDEVICETEXTURE;

struct STextureInfoData
{
  const void *pSysMem;
  uint32 SysMemPitch;
  uint32 SysMemSlicePitch;
};

struct STextureInfo
{
  uint16 m_nArraySize;
  uint8 m_nMSAASamples;
  uint8 m_nMSAAQuality;
  STextureInfoData *m_pData;
  STextureInfo()
  {
    m_nArraySize = 1;
    m_nMSAASamples = 1;
    m_nMSAAQuality = 0;
    m_pData = NULL;
  }
};

//===============================================================================================================

class CDeviceManager
{
  friend class CD3DRenderer;
  friend class CDeviceTexture;

private:
#ifndef NULL_RENDERER
  D3DDevice *m_pDevice;
#endif

public:
	enum 
	{
		USAGE_DEPTH_STENCIL = 0x20000,
		USAGE_RENDER_TARGET = 0x40000,
		USAGE_DYNAMIC = 0x80000,
		USAGE_AUTOGENMIPS = 0x100000,
		USAGE_STREAMING = 0x200000,
		USAGE_READBACK  = 0x400000,
		USAGE_STAGING  = 0x800000,
		USAGE_IMMUTABLE = 0x1000000,
		USAGE_DEFAULT = 0x2000000,
		USAGE_CPU_READ = 0x4000000,
		USAGE_CPU_WRITE = 0x8000000,
		USAGE_CPU_CACHED_MEMORY = 0x10000000,
		USAGE_CUSTOM = (USAGE_DEPTH_STENCIL | USAGE_RENDER_TARGET | USAGE_DYNAMIC |USAGE_AUTOGENMIPS |USAGE_READBACK),
	}; 

	enum 
	{ 
		BIND_VERTEX_BUFFER = 0x1,
		BIND_INDEX_BUFFER	= 0x2,
		BIND_CONSTANT_BUFFER = 0x4,
		BIND_SHADER_RESOURCE = 0x8,
		BIND_STREAM_OUTPUT = 0x10,
		BIND_RENDER_TARGET = 0x20,
		BIND_DEPTH_STENCIL = 0x40,
	};


#ifndef NULL_RENDERER
  CDeviceManager() { m_pDevice = NULL; }
  CDeviceManager(D3DDevice *pDevice);

  void AssignDevice(D3DDevice *pDevice) { m_pDevice = pDevice; }

	void rtInvalidate(CDeviceTexture* tex);
  HRESULT Create2DTexture(uint32 nWidth, uint32 nHeight, uint32 nMips, uint32 nUsage, D3DFormat Format, D3DPOOL Pool, LPDEVICETEXTURE* ppDevTexture, STextureInfo* pTI=NULL, bool bShouldBeCreated = false, int8 nPriority=0);
  HRESULT CreateCubeTexture(uint32 nSize, uint32 nMips, uint32 nUsage, D3DFormat Format, D3DPOOL Pool, LPDEVICETEXTURE* ppDevTexture, STextureInfo* pTI=NULL, bool bShouldBeCreated = false);
  HRESULT CreateVolumeTexture(uint32 nWidth, uint32 nHeight, uint32 nDepth, uint32 nMips, uint32 nUsage, D3DFormat Format, D3DPOOL Pool, LPDEVICETEXTURE* ppDevTexture, STextureInfo* pTI=NULL);

  HRESULT CreateBuffer(uint32 nSize, uint32 elemSize, int32 nUsage, int32 nBindFlags, D3DBuffer **ppBuff);





#ifdef DEVMAN_USE_STAGING_POOL
	D3DTexture* AllocateStagingTexture(D3DTexture* pForTex);
	void ReleaseStagingTexture(D3DTexture* pStagingTex);
#endif

#endif

#ifdef DEVMAN_USE_STAGING_POOL
private:
	struct StagingTextureDef
	{
		D3D11_TEXTURE2D_DESC desc;
		D3DTexture* pStagingTexture;

		friend bool operator == (const StagingTextureDef& a, const D3D11_TEXTURE2D_DESC& b)
		{
			return memcmp(&a.desc, &b, sizeof(b)) == 0;
		}
	};

	typedef std::vector<StagingTextureDef, stl::STLGlobalAllocator<StagingTextureDef> > StagingPoolVec;

private:
	StagingPoolVec m_stagingPool;
#endif
};

//====================================================================================================

struct STexLock
{
  uint8* pData;
  uint32 Pitch;
	STexLock() : pData(NULL), Pitch(0) { }
};
struct STexLockBox
{
  uint8* pData;
  uint32 RowPitch;
  uint32 SlicePitch;
	STexLockBox() : pData(NULL), RowPitch(0), SlicePitch(0) { }
};

// Texture lock flags
#define LF_READ    1
#define LF_WRITE   2
#define LF_DISCARD 4
#define LF_STREAMING 8




class CDeviceTexture

{
  friend class CDeviceManager;


#ifndef NULL_RENDERER
  D3DBaseTexture *m_pD3DTexture;
#if defined (DIRECT3D10) && !defined(PS3)
  D3DTexture *m_pStagingTexture;
#endif
	// for native hand-made textures
	void*						m_pBaseAddress;
	size_t					m_nBaseAllocatedSize;
#endif
  bool            m_bNoDelete; 
#ifdef DEVMAN_USE_STAGING_POOL
	bool						m_bStagingTextureAllocedOnLock;
#endif








public:
#ifndef NULL_RENDERER

	void free();

	D3DBaseTexture *GetBaseTexture();
	D3DTexture *Get2DTexture() { return (D3DTexture *)GetBaseTexture(); }
  D3DCubeTexture *GetCubeTexture() { return (D3DCubeTexture *)GetBaseTexture(); }
  D3DVolumeTexture *GetVolumeTexture() { return (D3DVolumeTexture *)GetBaseTexture(); }

	CDeviceTexture (): m_pD3DTexture(NULL), m_pBaseAddress(NULL), m_nBaseAllocatedSize(0), m_bNoDelete(false)



#if defined (DIRECT3D10) && !defined(PS3)
  , m_pStagingTexture(NULL)
#endif
#ifdef DEVMAN_USE_STAGING_POOL
	, m_bStagingTextureAllocedOnLock(false)
#endif
	{}
	CDeviceTexture (D3DBaseTexture *pBaseTexture): m_pD3DTexture(pBaseTexture), m_pBaseAddress(NULL), m_nBaseAllocatedSize(0), m_bNoDelete(false)



#if defined (DIRECT3D10) && !defined(PS3)
  , m_pStagingTexture(NULL)
#endif
#ifdef DEVMAN_USE_STAGING_POOL
	, m_bStagingTextureAllocedOnLock(false)
#endif
	{}

	CDeviceTexture (const CDeviceTexture& b);

  ~CDeviceTexture();

#if defined(DIRECT3D10) && !defined(PS3)
	void CopyToStagingTexture(uint32 nLevel, bool bFromLock = false);
#endif
  HRESULT LockRect(uint32 nLevel, STexLock& Data, uint32 nFlags);
  HRESULT LockRect(int32 nCubeSide, uint32 nLevel, STexLock& Data, uint32 nFlags);
  HRESULT LockBox(uint32 nLevel, STexLockBox& Data, uint32 nFlags);
  HRESULT UnlockRect(uint32 nLevel);
  HRESULT UnlockRect(int32 nCubeSide, uint32 nLevel);
	HRESULT UnlockBox(uint32 nLevel);
	void* GetBaseAddress() { return m_pBaseAddress; }
	size_t GetDeviceSize() const;
#endif






	void GetMemoryUsage(ICrySizer *pSizer) const
	{
		pSizer->AddObject(this, sizeof(*this));



	}

  int32 Release();
  void SetNoDelete(bool noDelete) { m_bNoDelete = noDelete; } 
};

class CDeviceVertexBuffer
{

};

class CDeviceIndexBuffer
{

};

#endif  _DeviceManager_H_
